package org.example.translation;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Element;
import org.dom4j.dom.DOMElement;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;

/**
 * 翻译实体。具体逻辑是由此类实现
 */
public abstract class AbsTranslationConsumer implements TranslationConsumer {

    protected final TranslationManager translationManager = TranslationManager.getInstance();

    @Override
    public void accept(StringsXMLData sourceXMLData, StringsXMLData translationXMLData) {
        //String部分替换
        System.out.println("替换string部分");
        for (String sourceKey : sourceXMLData.getSourceKV().keySet()) {
            //源数据条目
            Element sourceElement = sourceXMLData.getSourceKV().get(sourceKey);
            //翻译数据条目
            Element translationElement = translationXMLData.getSourceKV().get(sourceKey);
            //判断是否跳过翻译
            if (needSkipElement(sourceElement)) {
                //如果跳过翻译则进入下一轮循环
                onSkipTranslation(sourceElement);
                continue;
            }
            //是否需要翻译
            if (stringTranslationPredicate(sourceKey, sourceElement, translationElement)) {
                //源语言
                String query = getStringContainerData(sourceElement);
                TranslationResponseBean transitioned = onElementNeedTranslation(sourceXMLData, translationXMLData, query);
                if (checkTranslationResult(transitioned)) {
                    //空结果
                    onTranslationFailure(sourceXMLData, translationXMLData, sourceElement);
                    continue;
                }
                //翻译非空则进行数据填入
                if (translationElement != null) {//非空
                    translationElement.setText(transitioned.getTargetResult());
                    onStringElementChanged(translationElement);
                } else {//空
                    DOMElement newElement = new DOMElement("string");
                    newElement.setAttribute("name", sourceElement.attributeValue("name"));
                    newElement.setText(transitioned.getTargetResult());
                    //或许可能大概需要
                    newElement = onCreateNewStringElement(newElement);
                    //数据填入
                    translationXMLData.getSourceDocument().getRootElement().add(newElement);
                    onStringTranslationAppend(translationXMLData, newElement);
                }
            }
        }
        //StringArray部分替换，与string部分替换基本相同，稍微复杂一些
        for (String sourceKey : sourceXMLData.getSourceArrayKV().keySet()) {
            //提取源语言和翻译语言的值
            //list包含的是stringarray内包含item的列表
            List<?> sourceElement = sourceXMLData.getSourceArrayKV().get(sourceKey);
            //使用getOrDefault防止之后出现空对象
            List<?> translationElement = translationXMLData.getSourceArrayKV().getOrDefault(sourceKey, new ArrayList<>());
            //这两个element是stringarray对象
            Element sourceArrayElement = sourceXMLData.getSourceArray().get(sourceKey);
            Element translationArrayEelement = translationXMLData.getSourceArray().get(sourceKey);
            //这个是翻译过后的stringarray包含的item列表
            List<Element> translatedItemArray = new ArrayList<>();

            //判断是否需要进行翻译
            if (stringArrayTranslationPredicate(sourceArrayElement, translationArrayEelement, sourceElement, translationElement)) {
                //二话不说先去掉这个stringArray。在这里不再沿用处理sting条目一样的逻辑，要稍微复杂一些：
                //由于是一个套环，我的做法是去掉原有的stringArray节点，将原节点内包含的item节点重新生成一次
                //这样就不用过于考虑当源语言的item条目与翻译语言包含的条目数据、大小是否一致。
                //只根据源语言的条目数量生成对应翻译语言的条目
                //去掉这个stringarray
                boolean removed = translationXMLData.getSourceDocument().getRootElement().remove(translationArrayEelement);
                //新建一个stringArray的根dom节点
                DOMElement rootElement = new DOMElement("string-array");
                rootElement.setAttribute("name", sourceKey);
                if (translationArrayEelement == null) onCreateStringArrayElement(rootElement);
                //对这个源语言的stringarray节点进行遍历
                for (int i = 0; i < sourceElement.size(); i++) {
                    //原语言 stringarray内的index item条目
                    Element sourceItem = (Element) sourceElement.get(i);
                    //翻译语言 stringArray 内index item条目
                    Element translationItem = null;
                    //如果循环时翻译语言的条目数据比原语言的数量少，即翻译语言的此条目不存在
                    if (i >= translationElement.size()) {
                        //判断是否需要进行翻译。到此即和处理string标签逻辑一致了
                        if (stringArrayItemTranslationPredicate(sourceItem, translationItem)) {
                            //翻译
                            TranslationResponseBean transitioned = onElementNeedTranslation(sourceXMLData, translationXMLData, getStringContainerData(sourceItem));
                            //结果检查
                            if (checkTranslationResult(transitioned)) {
                                //空结果
                                onTranslationFailure(sourceXMLData, sourceXMLData, rootElement);
                                continue;
                            }
                            //此item不存在，新增item条目
                            translationItem = new DOMElement("item");
                            translationItem.setText(transitioned.getTargetResult());
                            translationItem = onCreateNewItemElement(translationItem);
                            translationItem.setText(transitioned.getTargetResult());
                        }
                    } else {
                        //循环时翻译语言条目和翻译语言条目都有对应index位置的item
                        translationItem = (Element) translationElement.get(i);
                        //判断是否需要翻译
                        if (stringArrayItemTranslationPredicate(sourceItem, translationItem)) {
                            //翻译
                            TranslationResponseBean transitioned = onElementNeedTranslation(sourceXMLData, translationXMLData, getStringContainerData(sourceItem));
                            //检查翻译结果
                            if (checkTranslationResult(transitioned)) {
                                //空结果
                                onTranslationFailure(sourceXMLData, sourceXMLData, rootElement);
                                continue;
                            }
                            //此item存在
                            translationItem.setText(translationItem.getText());
                        }
                    }
                    //统一放进已经翻译的list当中
                    translatedItemArray.add(translationItem);
                }
                //循环已经翻译的list，将数据放进新创建的stringArray节点当中
                for (Element element : translatedItemArray) {
                    Element appendElement = rootElement.addElement("item");
                    appendElement.setText(getStringContainerData(element));
                }
                //翻译doc重新加入此StringArray节点
                translationXMLData.getSourceDocument().getRootElement().add(rootElement);
            }
        }
    }

    private void onStringElementChanged(Element translationElement) {
        System.out.println("修改string" + translationElement.toString());
    }

    /**
     * 是否翻译此 string 标签
     *
     * @param sourceKey          源语言条目Name属性
     * @param sourceElement      源语言条目
     * @param translationElement 需要翻译的语言条目
     * @return true为翻译，false则跳过此条目不进行翻译
     */
    protected abstract boolean stringTranslationPredicate(String sourceKey, @NotNull Element sourceElement, @Nullable Element translationElement);

    /**
     * 是否需要翻译此 StringArray 标签
     *
     * @param sourceElement           源语言标签
     * @param translationElement      目标语言标签
     * @param sourceItemElements      源语言条目内容
     * @param translationItemElements 目标语言条目内容
     * @return true为翻译，false则跳过此条目不进行翻译
     */
    protected abstract boolean stringArrayTranslationPredicate(Element sourceElement, Element translationElement, List<?> sourceItemElements, List<?> translationItemElements);

    /**
     * 是否需要翻译此 item 标签
     *
     * @param sourceItemElement  源语言标签
     * @param translationElement 目标语言标签
     * @return true为翻译，false则跳过此条目不进行翻译
     */
    protected abstract boolean stringArrayItemTranslationPredicate(Element sourceItemElement, Element translationElement);

    /**
     * 判断是否带有 translatable 跳过翻译
     * @param element 数据条目
     * @return 是否跳过翻译
     */
    protected boolean needSkipElement(Element element) {
        //判断是否跳过翻译
        Attribute translatable = element.attribute("translatable");
        //如果跳过翻译则进入下一轮循环
        return (translatable != null && translatable.getValue().equals("false"));
    }

    /**
     * 当某个条目跳过翻译
     * @param itemElement 条目
     */
    protected void onSkipTranslation(Element itemElement) {
        System.out.println("跳过翻译:" + itemElement.attributeValue("name"));
    }

    /**
     * 获取条目包含的文字内容。由于android的文字不存在dom结构嵌套（这里只考虑string标签节点）
     * 因此不只是调用getText()，如果包含了dom结构，这将会获取到奇怪的数据。
     * 我们使用截取得到包含的文字数据、
     * @param sourceElement 条目
     * @return 条目内的文字
     */
    protected String getStringContainerData(Element sourceElement) {
        //源语言
        String query = sourceElement.getText();
        //防止string内置dom数据出现数据提取错误
        if (!sourceElement.elements().isEmpty()) {
            String xml = sourceElement.asXML();
            query = xml.substring(xml.indexOf(">") + 1, xml.lastIndexOf("<"));
        }
        return query;
    }

    /**
     * 翻译逻辑
     * @param sourceXMLData 原文件
     * @param translationXMLData 翻译文件
     * @param translationStr 翻译字符
     * @return 翻译后的数据
     */
    protected TranslationResponseBean onElementNeedTranslation(StringsXMLData sourceXMLData, StringsXMLData translationXMLData, String translationStr) {
        return translationManager.translation(new TranslationPostBean()
                .setSourceCountry(sourceXMLData.getCountry())
                .setSourceString(translationStr)
                .setTargetCountry(translationXMLData.getCountry()));
    }

    /**
     * 检查翻译结果是否为空
     * @param result 结果
     * @return 是否为空
     */
    protected boolean checkTranslationResult(TranslationResponseBean result) {
        return result == null || StringUtils.isEmpty(result.getTargetResult());
    }

    /**
     * 当翻译失败
     * @param sourceXMLData 原文件
     * @param translationXMLData 翻译文件
     * @param sourceElement 原条目
     */
    protected void onTranslationFailure(StringsXMLData sourceXMLData, StringsXMLData translationXMLData, Element sourceElement) {
        System.out.println("翻译失败：" + sourceElement.toString());
    }

    /**
     * 新建 string 条目。
     * @param newElement 新建的条目
     * @return 可能被你处理的条目
     */
    protected DOMElement onCreateNewStringElement(DOMElement newElement) {
        return newElement;
    }

    /**
     * 当翻译文件添加新的 string 条目
     * @param translationXMLData 翻译文件
     * @param newElement 新添加的条目
     */
    protected void onStringTranslationAppend(StringsXMLData translationXMLData, DOMElement newElement) {
        System.out.println("新建string：" + newElement.toString());
    }

    /**
     * 当翻译文件新增 string-array 条目
     * @param newElement 新增的条目
     */
    protected void onCreateStringArrayElement(DOMElement newElement) {
        System.out.println("新建string-array：" + newElement.toString());
    }

    /**
     * 当翻译文件 string-array 新增 item 条目
     * @param newElement 新增条目
     * @return 可能被你处理的条目
     */
    protected Element onCreateNewItemElement(Element newElement) {
        return newElement;
    }

}
